﻿using System.Xml;

namespace Truncation.Detector.WPF.XamlDom
{
    public class PositionXmlDocument : XmlDocument
    {
        IXmlLineInfo lineInfo;
        /// <summary>
        /// Creates a PositionXmlElement.
        /// </summary>
        public override XmlElement CreateElement(string prefix, string localName, string namespaceUri)
        {
            return new PositionXmlElement(prefix, localName, namespaceUri, this, lineInfo);
        }

        /// <summary>
        /// Creates a PositionXmlAttribute.
        /// </summary>
        public override XmlAttribute CreateAttribute(string prefix, string localName, string namespaceUri)
        {
            return new PositionXmlAttribute(prefix, localName, namespaceUri, this, lineInfo);
        }

        /// <summary>
        /// Loads the XML document from the specified <see cref="XmlReader"/>.
        /// </summary>
        public override void Load(XmlReader reader)
        {
            lineInfo = reader as IXmlLineInfo;
            try
            {
                base.Load(reader);
            }
            finally
            {
                lineInfo = null;
            }
        }
    }

    /// <summary>
    /// XML Element with line/column information.
    /// </summary>
    public class PositionXmlElement : XmlElement, IXmlLineInfo
    {
        private readonly int _lineNumber;
        private readonly int _linePosition;
        private readonly bool _hasLineInfo;

        internal PositionXmlElement(string prefix, string localName, string namespaceUri, XmlDocument doc, IXmlLineInfo lineInfo)
            : base(prefix, localName, namespaceUri, doc)
        {
            if (lineInfo != null)
            {
                _lineNumber = lineInfo.LineNumber;
                _linePosition = lineInfo.LinePosition;
                _hasLineInfo = true;
            }
        }

        /// <summary>
        /// Gets whether the element has line information.
        /// </summary>
        public bool HasLineInfo()
        {
            return _hasLineInfo;
        }

        /// <summary>
        /// Gets the line number.
        /// </summary>
        public int LineNumber
        {
            get { return _lineNumber; }
        }

        /// <summary>
        /// Gets the line position (column).
        /// </summary>
        public int LinePosition
        {
            get { return _linePosition; }
        }
    }

    /// <summary>
    /// XML Attribute with line/column information.
    /// </summary>
    public class PositionXmlAttribute : XmlAttribute, IXmlLineInfo
    {
        private readonly int _lineNumber;
        private readonly int _linePosition;
        private readonly bool _hasLineInfo;

        internal PositionXmlAttribute(string prefix, string localName, string namespaceUri, XmlDocument doc, IXmlLineInfo lineInfo)
            : base(prefix, localName, namespaceUri, doc)
        {
            if (lineInfo != null)
            {
                _lineNumber = lineInfo.LineNumber;
                _linePosition = lineInfo.LinePosition;
                _hasLineInfo = true;
            }
        }

        /// <summary>
        /// Gets whether the element has line information.
        /// </summary>
        public bool HasLineInfo()
        {
            return _hasLineInfo;
        }

        /// <summary>
        /// Gets the line number.
        /// </summary>
        public int LineNumber
        {
            get { return _lineNumber; }
        }

        /// <summary>
        /// Gets the line position (column).
        /// </summary>
        public int LinePosition
        {
            get { return _linePosition; }
        }
    }
}